/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cbir;

public class Histogram {

    public static double[] GetYUVHist(int[][][] DCTMatrix, int queryMethod) {

        //int yuv = 3; it is the first dimension of array DCTMatrix
        int width = DCTMatrix[0][0].length;//image width(horizontal)
        int height = DCTMatrix[0].length;//image height(vertical)
        int span = 8;//8 x 8 block

        //the 2 parameters below you can modify to scale the dimension of the histogram
        int binNum = 8;//number of bins in each color subspace
        int interval = 258000 / binNum;//the maximum and the minimum 129000 and -129000
        int blknum_h = width / span;
        int blknum_v = height / span;

        double[][][] ccv = new double[3][blknum_h][blknum_v];
        double[][][] cac = new double[3][blknum_h][blknum_v];

        double[] histColour = new double[binNum * binNum * binNum];
        double[] histCCV = new double[binNum * binNum * binNum];
        double[] histCAC = new double[binNum * binNum * binNum];

        double[] texture = new double[binNum * binNum * binNum];
        double[] segment = new double[256];

        for (int j = 0; j < blknum_h; j++) {
            for (int k = 0; k < blknum_v; k++) {
                int binY = DCTMatrix[0][k * span][j * span] / interval + 4;//calculate where the DC coefficient locates in Y subspace
                if (binY >= 8) {
                    binY = 7;
                } else if (binY <= -1) {
                    binY = 0;
                }
                int binU = DCTMatrix[1][k * span][j * span] / interval + 4;//calculate where the DC coefficient locates in U subspace
                if (binU >= 8) {
                    binU = 7;
                } else if (binU <= -1) {
                    binU = 0;
                }
                int binV = DCTMatrix[2][k * span][j * span] / interval + 4;//calculate where the DC coefficient locates in V subspace
                if (binV >= 8) {
                    binV = 7;
                } else if (binV <= -1) {
                    binV = 0;
                }
                histColour[binY * binNum * binNum + binU * binNum + binV]++;
                cac = fillColorMatrix(binY, binU, binV, blknum_h, blknum_v, j, k);
                ccv = fillColorMatrix(binY, binU, binV, blknum_h, blknum_v, j, k);
            }
        }
        if (queryMethod == 1) {
            histCCV = colorCoherenceVector(histColour, ccv, blknum_h, blknum_v);
        }

        if (queryMethod == 2) {
            histCAC = colorAutoCorrelogram(histColour, cac, blknum_h, blknum_v);
        }
        if (queryMethod == 3) {
            texture = textureAC(histColour, DCTMatrix);
        }
        if (queryMethod == 4) {
            segment = segment(histColour, DCTMatrix, DCTMatrix);
        }

        if (queryMethod == 0) {
            return histColour;
        } else if (queryMethod == 1) {
            return histCCV;
        } else if (queryMethod == 2) {
            return histCAC;
        } else if (queryMethod == 3) {
            return texture;
        } else {
            return segment;
        }

    }

    public static double[][][] fillColorMatrix(int binY, int binU, int binV, int blknum_h, int blknum_v, int j, int k) {
        double[][][] colourMatrix = new double[3][blknum_h][blknum_v];
        colourMatrix[0][j][k] = binY; //putting > imptance in Y
        colourMatrix[1][j][k] = binU;
        colourMatrix[2][j][k] = binV;

        return colourMatrix;
    }

    public static double[] colorCoherenceVector(double[] hist, double[][][] ccv, int blknum_h, int blknum_v) {
        int binNum = 8;
        int temp = 0;
        int large = 0;
        for (int i = 0; i < binNum - 1; i++) {
            for (int j = 0; j < binNum - 1; j++) {
                for (int k = 0; k < binNum - 1; k++) {

                    int increment = 0;
                    do {
                        increment++;
                        if (ccv[0][i][j] == ccv[0][i][j + increment] && ccv[1][i][j] == ccv[1][i][j + increment] && ccv[2][i][j] == ccv[2][i][j + increment]) {
                            temp++;
                        } else if (ccv[0][i][j] == ccv[0][i + increment][j] && ccv[1][i][j] == ccv[1][i + increment][j] && ccv[2][i][j] == ccv[2][i + increment][j]) {
                            temp++;
                        } else {
                            large = temp;
                        }
                        temp = 0;
                    } while (temp != 0 && increment + i < 8);
                    if (large > 5) {
                        hist[i * binNum * binNum + j * binNum + k] = hist[i * binNum * binNum + j * binNum + k] * 7 / 10; //coherent region
                    } else {
                        hist[i * binNum * binNum + j * binNum + k] = hist[i * binNum * binNum + j * binNum + k] * 3 / 10; //isolated region
                    }
                }
            }
        }
        return hist;
    }

    public static double[] colorAutoCorrelogram(double[] hist, double[][][] colourMatrix, int blknum_h, int blknum_v) {
        double[] cac = new double[Math.max(blknum_h, blknum_v)];
        for (int distance = 1; distance < cac.length; distance += 2) {
            for (int i = 0; i < blknum_h; i++) {
                for (int j = 0; j < blknum_v; j++) {
                    int total = 0;
                    int match = 0;
                    for (int k = distance * -1; k <= distance; k += (2 * distance)) {
                        for (int m = distance * -1; m <= distance; m++) {
                            if (i + k >= 0 && j + m >= 0 && i + k < blknum_h && j + m < blknum_v) {
                                total++;
                                if (colourMatrix[0][i][j] == colourMatrix[0][i + k][j + m] && colourMatrix[1][i][j] == colourMatrix[1][i + k][j + m] && colourMatrix[2][i][j] == colourMatrix[2][i + k][j + m]) {
                                    match++;
                                }
                            }
                        }
                    }
                    for (int k = distance * -1 + 1; k < distance; k++) {
                        for (int m = distance * -1; m <= distance; m += (2 * distance)) {
                            if (i + k >= 0 && j + m >= 0 && i + k < blknum_h && j + m < blknum_v) {
                                total++;
                                if (colourMatrix[0][i][j] == colourMatrix[0][i + k][j + m] && colourMatrix[1][i][j] == colourMatrix[1][i + k][j + m] && colourMatrix[2][i][j] == colourMatrix[2][i + k][j + m]) {
                                    match++;
                                }
                            }
                        }
                    }
                    if (total != 0 && match != 0) {
                        cac[distance] += match / (1.0 * total);
                    }

                }
            }
        }
        for (int i = 0; i < hist.length; i++) {
            hist[i] = 0.1 * cac[i % cac.length] + 0.9 * hist[i];
        }
        return hist;
    }

    public static double[] textureAC(double[] hist, int[][][] DCTMatrix) {
        int binNum = 8;
        double[] histAC = new double[binNum * binNum * binNum];

        int width = DCTMatrix[0][0].length;//image width(horizontal)
        int height = DCTMatrix[0].length;//image height(vertical)
        double[][] ac = new double[width / 8][height / 8];
        for (int i = 0; i < width / 8; i++) {
            for (int j = 0; j < height / 8; j++) {
                for (int k = 0; k < 8; k++) {
                    for (int m = 0; m < 8; m++) {
                        ac[i][j] += Math.abs(DCTMatrix[0][i + k][j + m]);
                        System.out.print(ac[i][j]);
                    }
                }
                //System.out.println("energy " + ac[i][j]);
                int threshold = 150000;
                if (ac[i][j] > threshold) {

                    histAC[((DCTMatrix[0][(j * 8) % height][(i * 8) % width] / 32250 + 4) * binNum * binNum
                            + (DCTMatrix[1][(j * 8) % height][(i * 8) % width] / 32250 + 4) * binNum
                            + (DCTMatrix[2][(j * 8) % height][(i * 8) % width] / 32250 + 4))]++;
                }
            }
        }
        for (int i = 0; i < histAC.length; i++) {
            histAC[i] = 0.1 * hist[i] + 0.9 * histAC[i];
        }
        return histAC;
    }

    //this is the sub images. which is one of them is 4 by 4.
    //basis is both the width and the height must be odd or even
    public static int[][][] getSubImage(int[][][] DCTMatrix, int y, int x) {
        int dim = 4;
        int[][][] sub = new int[DCTMatrix.length][dim][dim];
        for (int i = 0; i < DCTMatrix.length; i++) {
            for (int j = 0; j < dim; j++) {
                for (int k = 0; k < dim; k++) {
                    sub[i][j][k] = DCTMatrix[i][dim * y + j][dim * x + k];
                }
            }
        }
        return sub;
    }

    //then finding the center so that there is heavier weightage in the center.
    public static int[][][] getMid(int width, int height, int[][][] DCTMatrix) {
//        int dim = 4; //default
        int[][][] mid = new int[DCTMatrix.length][height][width];
        for (int i = 0; i < DCTMatrix.length; i++) {
            for (int j = 0; j < height; j++) {
                for (int k = 0; k < width; k++) {
                    mid[i][j][k] = DCTMatrix[i][(DCTMatrix[0].length - height) / 2 + j][(DCTMatrix[0][0].length - width) / 2 + k];
                }
            }
        }
        return mid;

    }

    public static double[] segment(double[] hist, int[][][] DCTMatrix, int[][][] DCTMatrix2) {
        //assume normalised the DCTs

        int width = DCTMatrix[0][0].length;
        int height = DCTMatrix[0].length;
        int dim = 4;
        int numOfSubImages_width = (int) Math.ceil(width / dim);
        int numOfSubImages_height = (int) Math.ceil(height / dim);
        double[] dist = new double[8*8*8]; //[numOfSubImages_height*numOfSubImages_width];
        int[][][] temp = new int[DCTMatrix.length][numOfSubImages_height][numOfSubImages_width];
        int[][][] temp2 = new int[DCTMatrix.length][numOfSubImages_height][numOfSubImages_width];
        double[][][] diff = new double[DCTMatrix.length][numOfSubImages_height][numOfSubImages_width]; 
        for (int k = 0; k < DCTMatrix.length; k++) {
            for (int j = 0; j < dim; j++) {
                for (int i = 0; i < dim; i++) {
                    temp = getMid(width, height, DCTMatrix);
                    temp2 = getSubImage(DCTMatrix2, j, i);
                    //findin distance of the corresponding sub window.
                    diff[k][j][i] = (temp[k][j][i] - temp2[k][j][i]) * (temp[k][j][i] - temp2[k][j][i]);
                }
            }
        }
        //adding up across the yuv sections.
        for (int j = 0; j < diff[0].length; j++) {
            for (int i = 0; i < diff[0][0].length; i++) {
                dist[j] = Math.sqrt(diff[0][j][i] + diff[1][j][i] + diff[2][j][i]);
            }
        }

        for (int i = 0; i < dist.length; i++) {
            dist[i] = 0.1 * hist[i] + 0.9 * dist[i];
        }


//                            histAC[((DCTMatrix[0][(j * 8) % height][(i * 8) % width] / 32250 + 4) * binNum * binNum
//                            + (DCTMatrix[1][(j * 8) % height][(i * 8) % width] / 32250 + 4) * binNum
//                            + (DCTMatrix[2][(j * 8) % height][(i * 8) % width] / 32250 + 4))]++;
//        double[][] diff = new double[3][256]; //16*16
//
//        int l=0;
//        for (int k = 0; k < 3; k++) { //y u v
////            for(int s = 1; s<=16; s++) { //number of subimages. so basically ths first 16 are using the first block to compare with the rest
//                for (int j = 0; j < numOfSubImages_height; j ++) {
//                    for (int i = 0; i < numOfSubImages_width; i ++) {
//                        for (int m = 0; m < numOfSubImages_height; m++) {
//                            for(int n = 0; n < numOfSubImages_width; n++)
//                        {
//                        diff[k][l] = (DCTMatrix[k][j][i] - DCTMatrix2[k][m][n])*(DCTMatrix[k][j][i] - DCTMatrix2[k][m][n]);
//                        l++;
//                    }
//                }
//            }
//
//        }
//                l=0;
//        }
//        for(int i = 0; i<diff[0].length; i++)
//        {
//            dist[i] = Math.sqrt(diff[0][i] + diff[1][i]+ diff[2][i]);
//        }

        return dist;
    }

    // histogram intersection
    public static double Intersection(double[] h1, double[] h2) {
//        int Y[] = new int[512];
//        int U[] = new int[512];
//        int V[] = new int[512];
//
//        for (int i = 0; i < 512; i++) {
//            Y[i] = i;
//            U[i] = i;
//            V[i] = i;
//        }
//
//
//        double[][] mSim = new double[512][512];
//        for (int i = 0; i < 512; i++) {
//            for (int j = 0; j < 512; j++) {
//                double temp = Math.sqrt((Y[i] - Y[j]) * (Y[i] - Y[j]) + (U[i] - U[j]) * (U[i] - U[j]) + (V[i] - V[j]) * (V[i] - V[j]));
//                if (temp > 51.2) {
//                    mSim[i][j] = 0;
//                } else {
//                    mSim[i][j] = 1 - temp / 51.2;
//                }
//            }
//        }


        int norm1 = 0, norm2 = 0;
        for (int i = 0; i < Math.min(h1.length, h2.length); i++) {
            norm1 += h1[i];
            norm2 += h2[i];
        }
        double intersection = 0.0;

        double[] norm_h1 = new double[h1.length];
        double[] norm_h2 = new double[h1.length];

        //normalize each histogram
        for (int i = 0; i < Math.min(h1.length, h2.length); i++) {

            norm_h1[i] = (double) h1[i] / (double) norm1;
            norm_h2[i] = (double) h2[i] / (double) norm2;
        }

        for (int i = 0; i < Math.min(h1.length, h2.length); i++) {
//            if (i < h1.length - 1) {
//                if (norm_h1[i] > norm_h2[i]) {
//                    intersection += norm_h2[i] * mSim[i][i + 1];
//                } else {
//                    intersection += norm_h1[i] * mSim[i][i + 1];
//                }
//            } else {
            if (norm_h1[i] > norm_h2[i]) {
                intersection += norm_h2[i];
            } else {
                intersection += norm_h1[i];
            }
//            }

        }

        return intersection;


    }
}

